//  Copyright (c) 2007-2017 Hartmut Kaiser
//
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <hpx/performance_counters/counters.hpp>
#include <hpx/runtime/components/binpacking_distribution_policy.hpp>

#include <cstddef>
#include <cstdint>
#include <string>
#include <utility>
#include <vector>

namespace hpx { namespace components
{
    namespace detail
    {
        std::vector<std::size_t>
        get_items_count(std::size_t count, std::vector<std::uint64_t> const& values)
        {
            std::size_t maxcount = 0;
            std::size_t existing = 0;

            for (std::uint64_t value: values)
            {
                maxcount = (std::max)(maxcount, std::size_t(value));
                existing += std::size_t(value);
            }

            // distribute the number of components to create in a way, so that
            // the overall number of component instances on all localities is
            // approximately the same
            std::size_t num_localities = values.size();

            // calculate the number of new instances to create on each of the
            // localities
            std::vector<std::size_t> to_create(num_localities, 0);

            bool even_fill = true;
            while (even_fill)
            {
                even_fill = false;
                for (std::size_t i = 0; i != num_localities; ++i)
                {
                    if(values[i] + to_create[i] >= maxcount) continue;
                    even_fill = true;

                    ++to_create[i];
                    --count;
                    if (count == 0) break;
                }
            }
            std::size_t i = 0;
            while (count != 0)
            {
                ++to_create[i];
                i = (i + 1) % num_localities;
                --count;
            }

            return to_create;
        }

        hpx::future<std::vector<std::uint64_t> >
        retrieve_counter_values(
            std::vector<performance_counters::performance_counter> && counters)
        {
            using namespace hpx::performance_counters;

            std::vector<hpx::future<std::uint64_t> > values;
            values.reserve(counters.size());

            for (performance_counter const& counter: counters)
                values.push_back(counter.get_value<std::uint64_t>());

            return hpx::dataflow(hpx::launch::sync,
                [](std::vector<hpx::future<std::uint64_t>> && values)
                 -> std::vector<std::uint64_t>
                {
                    return hpx::util::unwrap(values);
                },
                std::move(values));
        }

        hpx::future<std::vector<std::uint64_t> > get_counter_values(
            std::string component_name, std::string const& counter_name,
            std::vector<hpx::id_type> const& localities)
        {
            using namespace hpx::performance_counters;

            // create performance counters on all localities
            std::vector<performance_counter> counters;
            counters.reserve(localities.size());

            if (counter_name[counter_name.size()-1] == '@')
            {
                std::string name(counter_name + component_name);

                for (hpx::id_type const& id: localities)
                    counters.push_back(performance_counter(name, id));
            }
            else
            {
                for (hpx::id_type const& id: localities)
                    counters.push_back(performance_counter(counter_name, id));
            }

            return hpx::dataflow(
                &retrieve_counter_values, std::move(counters));
        }

        hpx::id_type const& get_best_locality(
            hpx::future<std::vector<std::uint64_t> > && f,
            std::vector<hpx::id_type> const& localities)
        {
            std::vector<std::uint64_t> values = f.get();

            std::size_t best_locality = 0;
            std::uint64_t min_value =
                (std::numeric_limits<std::uint64_t>::max)();

            for (std::size_t i = 0; i != values.size(); ++i)
            {
                if (min_value > values[i])
                {
                    min_value = values[i];
                    best_locality = i;
                }
            }

            return localities[best_locality];
        }
    }
}}
